package bma.common.netty.thrift.core;

import java.util.concurrent.Executor;

import bma.common.langutil.concurrent.ProcessTimerTaskAbstract;
import bma.common.langutil.concurrent.RapidUpdateTimeline;
import bma.common.langutil.concurrent.RapidUpdateTimeline.Processor;
import bma.common.langutil.concurrent.TimerManager;

public class NettyThriftTimeout {

	public static abstract class Timeout {
		protected int channelId;
		protected int seqid;

		public Timeout(int channelId, int seqid) {
			super();
			this.channelId = channelId;
			this.seqid = seqid;
		}

		@Override
		public boolean equals(Object obj) {
			if (obj instanceof Timeout) {
				Timeout o = (Timeout) obj;
				return o.channelId == channelId && o.seqid == seqid;
			}
			return super.equals(obj);
		}

		@Override
		public int hashCode() {
			return this.seqid;
		}

		public abstract void timeout();
	}

	public static class EmptyTimeout extends Timeout {

		public EmptyTimeout(int channelId, int seqid) {
			super(channelId, seqid);
		}

		@Override
		public void timeout() {

		}
	}

	protected Executor executor;

	protected TimerManager timer;

	protected RapidUpdateTimeline<Timeout> timeline;

	// runtime
	protected boolean active;

	protected Processor<Timeout> timeoutProcessor = new Processor<Timeout>() {
		@Override
		public void process(Timeout item) {
			item.timeout();
		}
	};

	public NettyThriftTimeout() {
		super();
	}

	public TimerManager getTimer() {
		return timer;
	}

	public void setTimer(TimerManager timer) {
		this.timer = timer;
	}

	public RapidUpdateTimeline<Timeout> getTimeline() {
		return timeline;
	}

	public void setTimeline(RapidUpdateTimeline<Timeout> timeline) {
		this.timeline = timeline;
	}

	public Executor getExecutor() {
		return executor;
	}

	public void setExecutor(Executor executor) {
		this.executor = executor;
	}

	protected void timer(long time) {
		ProcessTimerTaskAbstract task = new ProcessTimerTaskAbstract(time, true) {

			@Override
			public void run() {
				getExecutor().execute(new Runnable() {
					@Override
					public void run() {
						long nexttm = 0;
						synchronized (timeline) {
							nexttm = timeline.processTickItems(
									System.currentTimeMillis(),
									timeoutProcessor);
							if (timeline.isEmpty()) {
								active = false;
								nexttm = -1;
							}
						}
						if (nexttm > 0) {
							timer(nexttm);
						}
					}
				});
			}
		};
		TimerManager.checkInstance(getTimer()).postTimerTask(task);
	}

	public void add(long tm, Timeout obj) {
		boolean a = false;
		long time = System.currentTimeMillis() + tm;
		synchronized (this.timeline) {
			this.timeline.add(time, obj);
			a = active;
			active = true;
		}
		if (!a) {
			timer(this.timeline.timeTick(time));
		}
	}

	public void remove(int channelId, int seqid) {
		this.timeline.remove(new EmptyTimeout(channelId, seqid));
	}

	public void clear() {
		this.timeline.clear(null);
	}
}
